class Viewer {
    #thumbnailCache;
    #renderPagesCache;
    #pageViewportCache;
    #annotateDataCache;
    static defaultPage = {
        width: 595, height: 842
    };

    constructor(options) {

        // 清除缓存
        localStorage.clear();
        // 渲染页面缓存
        this.#renderPagesCache = new Set();
        // 页面视图缓存
        this.#pageViewportCache = new Map();
        // 签批内容缓存
        this.#annotateDataCache = new Map();
        // 初始化缩略图缓存
        this.#thumbnailCache = new Map();

        // 配置选项初始化
        this.urlType = options.urlType;
        this.url = options.url ?? "./test.pdf";
        this.scale = options.scale ?? 2;
        this.pageZoom = options.pageZoom ?? 'page-width';
        this.pdfBackgroundColor = options.pdfBackgroundColor ?? "#ffffff";
        this.isAnnotationLayer = options.isAnnotationLayer ?? false;
        this.isEraser = options.isEraser ?? false;
        this.penType = options.penType ?? 'pen';
        this.drawerElementId = options.drawerElementId;
        // 签批文件名称
        this.signFileName = options.signFileName ?? "";

        this.viewerContainer = document.getElementById('viewer-container');
        this.saveButton = document.getElementById('save_pdf');
        this.touchCircle = document.getElementById('touch-circle');

        // 渲染缓存配置
        this.preloadPages = 7;
        // 最大缓存页数
        this.maxCacheSize = 15;
        // 当前页码
        this.pageNumber = 0;
        // 前一页码
        this.preNumber = 0;
        // 总页码
        this.totalPages = 0;
        // pdf 文档
        this.pdfDocument = null;
        // pdf 宽度和高度
        this.pdfWidth = 0;
        this.pdfHeight = 0;

        // 绘制笔触粗细
        this.minLineWidth = 1;
        this.maxLineWidth = 2.5;
        // 渲染队列
        this.renderQueue = [];
        // 渲染状态
        this.rendering = false;
        // 当前签批列表
        this.signatureList = new Map();

        // 初始化渲染缩放
        this.outputScale = this.getOutputScale();
        this.transform = this.outputScale.scaled ? [this.outputScale.sx, 0, 0, this.outputScale.sy, 0, 0] : null;
        this.sfx = this.approximateFraction(this.outputScale.sx);
        this.sfy = this.approximateFraction(this.outputScale.sy);

        // 窗口大小
        this.window = {width: $(window).width(), height: $(window).height()};
        // 抽屉状态
        this.drawerOpen = false;

        // 获取视图容器和pdf.worker路径配置
        pdfjsLib.GlobalWorkerOptions.workerSrc = './static/plugin/pdfjs/pdf.worker.js';
        pdfjsLib.GlobalWorkerOptions.cMapPacked = true;

        // 初始化 IntersectionObserver 和滚动监听
        this.#setupIntersectionObserver();
        this.#setupScrollListener();

        window.addEventListener('resize', async () => {
            await this.zoomPage();
        });
    }

    // 配置 IntersectionObserver 监听页面的可见性
    #setupIntersectionObserver() {
        const options = {
            root: this.viewerContainer, // 默认为视口
            threshold: 0.3 // 超过 30% 可见时触发
        };

        this.observer = new IntersectionObserver((entries) => {
            entries.forEach(async (entry) => {
                if (entry.isIntersecting) {
                    this.preNumber = this.pageNumber;
                    this.pageNumber = entry.target.dataset.page;
                    this.#setCurrentPage();
                    if (this.drawerOpen) {
                        this.changeThumbnailActive(this.pageNumber);
                    }
                    this.changePageButton();
                }
            });
        }, options);
    }

    // 配置滚动监听器
    #setupScrollListener() {
        let scrollTimeout;
        document.getElementById('viewer-container').addEventListener("scroll", () => {
            if (scrollTimeout) {
                clearTimeout(scrollTimeout);
            }
            scrollTimeout = setTimeout(async () => {
                // 等待滚动停止500ms后才触发页面切换操作
                await this.checkPageChange();
            }, 150);
        });
    }

    /**
     * 设置总页数
     */
    #setPageTotal() {
        document.getElementById("total-page").innerHTML = this.totalPages.toString();
    }

    /**
     * 设置当前页码
     */
    #setCurrentPage() {
        document.getElementById('current-page').innerHTML = this.pageNumber;
    }


    /**
     * 创建静态方法，用于创建新的Viewer实例
     * @param options
     * @returns {Promise<Viewer>}
     */
    static async create(options) {
        const viewer = new Viewer(options);
        await viewer.#init();
        viewer.changePageButton();
        return viewer;
    }

    /**
     * 初始化加载文档，设置总页码
     * @returns {Promise<void>}
     */
    async #init() {
        try {
            // 检查URL是否有效
            if (!this.url) {
                throw new Error('Invalid URL for loading PDF document');
            }
            // 加载PDF文档
            const pdfDocumentLoadingTask = pdfjsLib.getDocument({
                url: this.url,
                rangeChunkSize: 32768,
                disableStream: false,
                disableFontFace: true
            });

            // 等待文档加载完成
            this.pdfDocument = await pdfDocumentLoadingTask.promise;
            this.totalPages = this.pdfDocument.numPages;

            // 设置页面总数
            this.#setPageTotal(this.totalPages);

            // 提前获取 DOM 元素
            const viewerContainer = this.viewerContainer;
            const drawerElement = document.getElementById('drawer');

            // 创建文档片段，用于批量操作 DOM
            const pageFragment = document.createDocumentFragment();
            const thumbnailFragment = document.createDocumentFragment();

            // 遍历所有页面，直接调用同步方法
            for (let i = 0; i < this.totalPages; i++) {
                // 同步调用 createPageContainer，直接添加到 pageFragment
                const pageContainer = await this.createPageContainer(i);
                pageFragment.appendChild(pageContainer);

                // 同步调用 createThumbnail，直接添加到 thumbnailFragment
                const thumbnailDiv = this.createThumbnail(i);
                thumbnailFragment.appendChild(thumbnailDiv);
            }
            // 批量将页面容器和缩略图容器插入到 DOM 中
            viewerContainer.appendChild(pageFragment);
            drawerElement.appendChild(thumbnailFragment);

            // 预加载附近的页面
            await this.preloadPagesAround(1);
        } catch (error) {
            console.error('Error during initialization:', error);
            // 根据需要处理错误，比如显示错误提示
        }
    }

    /**
     * 创建页面容器
     * @param pageIndex
     * @returns {Promise<HTMLDivElement>}
     */
    async createPageContainer(pageIndex) {
        let pageContainer = document.createElement('div');
        pageContainer.classList.add('page-container');
        let pageNumber = pageIndex + 1;

        let page = await this.pdfDocument.getPage(pageNumber);
        let viewport = page.getViewport({
            scale: this.scale,
        });
        this.#pageViewportCache.set(pageNumber, {
            width: viewport.width,
            height: viewport.height,
        });
        let {
            pageWidth, pageHeight
        } = viewport.rawDims;
        this.#pageViewportCache.set(pageNumber, {
            width: pageWidth,
            height: pageHeight,
        });
        // 计算宽度和高度的函数
        const calculateDimensions = (width, height, pageWidth, pageHeight) => {
            const w = `${width}px`;
            const h = `${height}px`;
            const widthStr = `${w}`;
            const heightStr = `${h}`;

            return {widthStr, heightStr};
        };

        let width, height;

        if (viewport.rotation % 180 === 0) {
            // 竖屏
            width = this.window.width;
            height = (this.window.width / pageWidth) * pageHeight;
        } else {
            // 横屏
            width = this.window.width;
            height = (this.window.width / pageHeight) * pageWidth;
        }

        // 使用提取的函数计算并获取最终宽度和高度字符串
        const {widthStr, heightStr} = calculateDimensions(width, height, pageWidth, pageHeight);
        // 根据页面缩放模式设置页面容器的样式
        if (this.pageZoom === 'page-width') {
            pageContainer.style.width = widthStr;
            pageContainer.style.height = heightStr;
            pageContainer?.style.setProperty("--scale-factor", Math.round(this.window.width / pageWidth).toString());
        } else {
            this.setPageContainerSize(pageContainer);
        }
        pageContainer.style.setProperty('--loading-display', 'block');
        pageContainer.dataset.page = pageNumber.toString();
        pageContainer.classList.add('loading');

        if (!this.observer) {
            // 这里判断observer是否存在，如果不存在，执行某些操作
            while (!this.observer) {
                // 如果 this.observer 还不存在，你可以等待一段时间或执行其他操作
                // 比如延时检查，或重新初始化 observer
                await new Promise(resolve => setTimeout(resolve, 100)); // 延迟100ms再检查
            }
        }
        this.observer.observe(pageContainer)

        return pageContainer;
    }

    /**
     * 添加页面到渲染队列中
     * @param pageNumber
     * @returns {Promise<void>}
     */
    async addToRenderQueue(pageNumber) {
        // 确保 pageNumber 是有效的
        if (typeof pageNumber !== 'number') {
            console.error('Invalid page number:', pageNumber);
            return;
        }
        if (!this.renderQueue.includes(pageNumber)) {
            this.renderQueue.push(pageNumber);
            await this.renderQueuePage();
        }
    }

    /**
     * 渲染队列中的页面
     * @returns {Promise<void>}
     */
    async renderQueuePage() {
        // 渲染中，退出
        if (this.rendering) {
            return;
        }
        // 渲染队列不为空
        while (this.renderQueue.length > 0) {

            let currentPage = this.renderQueue[0];
            try {
                if (currentPage > 0 && currentPage <= this.totalPages) {
                    // 渲染页面
                    await this.renderPage(currentPage);
                }
                this.#renderPagesCache = new Set(Array.from(this.#renderPagesCache).sort((a, b) => a - b));
                this.renderQueue.shift();
            } catch (error) {
                console.error('Error rendering page', error);
            } finally {
                this.rendering = false;
            }
        }
    }

    /**
     * PDF页面绘制
     * @param currentPageNumber
     */
    async renderPage(currentPageNumber) {
        let that = this;
        if (this.#renderPagesCache.size >= this.maxCacheSize) {
            this.renderQueue = []; // 清空队列
            return; // 退出循环
        }
        // 避免重复渲染
        if (this.#renderPagesCache.has(currentPageNumber)) {
            return;
        }
        if (document.querySelector(`.page-canvas[data-page="${currentPageNumber}"]`) !== null) {
            return;
        }
        let {
            pageCanvas
        } = await this.createPageDom(currentPageNumber);
        let thisCtx = pageCanvas.getContext('2d', {alpha: false});
        if (this.pdfDocument !== null) {
            let page = await this.pdfDocument.getPage(currentPageNumber);
            let viewport = page.getViewport({
                scale: this.scale, rotation: page.rotate
            });
            this.pdfWidth = viewport.width;
            this.pdfHeight = viewport.height;

            // 设置pdfCanvas默认宽高
            pageCanvas.width = this.roundToDivide(this.pdfWidth * this.outputScale.sx, this.sfx[0]);
            pageCanvas.height = this.roundToDivide(this.pdfHeight * this.outputScale.sy, this.sfy[0]);
            const renderContext = {
                canvasContext: thisCtx,
                transform: this.transform,
                viewport: viewport,
                background: this.pdfBackgroundColor
            };
            requestAnimationFrame(() => {
                page.render(renderContext).promise.then(async () => {
                    // 缓存当前页面的渲染状态
                    that.#renderPagesCache.add(currentPageNumber);

                    // 隐藏加载提示
                    that.hideLoading(currentPageNumber);

                    // 缓存 DOM 查询，避免重复查询
                    const pageCanvas = document.querySelector(`.page-canvas[data-page="${currentPageNumber}"]`);

                    // 显示当前页面的 canvas
                    pageCanvas.style.display = 'block';

                    // 合并条件判断，减少重复代码
                    const shouldCreateLayer = !that.signatureList.has(currentPageNumber) &&
                        (that.isAnnotationLayer || that.#annotateDataCache.has(currentPageNumber));

                    // 如果满足条件，创建层 DOM
                    if (shouldCreateLayer) {
                        await that.createLayerDom(currentPageNumber);
                    }
                });
            });
        }
    }

    /**
     * 设置页面容器的大小
     * @param pageContainer
     */
    setPageContainerSize(pageContainer) {
        // 根据页面宽高比和窗口宽高比设置页面容器的大小
        if (this.pdfWidth / this.pdfHeight < this.window.width / this.window.height) {
            pageContainer.style.width = 'auto';
            pageContainer.style.height = '100%';
        } else {
            pageContainer.style.width = '100%';
            pageContainer.style.height = 'auto';
        }
    }

    /**
     * 创建缩略图
     * @param pageIndex
     * @returns {HTMLDivElement}
     */
    createThumbnail(pageIndex) {
        const thumbnailDiv = document.createElement('div');
        thumbnailDiv.classList.add('drawer-item');
        thumbnailDiv.dataset.page = (pageIndex + 1).toString();
        return thumbnailDiv;
    }


    /**
     * 预加载页面
     * @param currentPageNumber
     * @returns {Promise<void>}
     */
    async preloadPagesAround(currentPageNumber) {
        currentPageNumber = parseInt(currentPageNumber);
        for (let i = (currentPageNumber - this.preloadPages); i <= currentPageNumber + this.preloadPages; i++) {
            if (i >= 1 && i <= this.totalPages) {
                await this.addToRenderQueue(i)  // renderPage 是你自己实现的渲染页面函数
            }
        }
    }

    /**
     * 校验页面切换
     */
    async checkPageChange() {
        const currentPage = parseInt(this.pageNumber);

        const halfCacheSize = Math.floor(this.maxCacheSize / 2);
        const minPage = currentPage - halfCacheSize;
        const maxPage = currentPage + halfCacheSize;

        // 获取当前缓存的页面集合
        const cachedPages = Array.from(this.#renderPagesCache);

        // 找出需要删除的页面
        const deletePage = cachedPages.filter(item => item < minPage || item > maxPage);

        // 计算需要添加的页面
        const addPages = [];
        for (let i = minPage; i <= maxPage; i++) {
            if (!this.#renderPagesCache.has(i)) {
                addPages.push(i);
            }
        }

        // 执行删除页面的操作
        await Promise.all(deletePage.map(page => {
            this.removeLayerDom(page);
            const canvas = document.querySelector(`.page-canvas[data-page="${page}"]`);
            if (canvas) {
                const context = canvas.getContext('2d');
                context.clearRect(0, 0, canvas.width, canvas.height);
                canvas.remove();
                this.#renderPagesCache.delete(page);
            }

            const pageContainer = document.querySelector(`.page-container[data-page="${page}"]`);
            if (pageContainer) {
                pageContainer.classList.add('loading');
            }
        }));

        // 执行添加页面的操作
        await Promise.all(addPages.map(page => {
            this.addToRenderQueue(page);
        }));
    }


    /**
     * 生成指定页面的缩略图
     * @param currentPageNumber
     * @param scale
     * @param beforeOrAfter
     * @returns {Promise<boolean>}
     */
    async generateThumbnail(currentPageNumber, scale, beforeOrAfter = 'after') {
        let that = this;
        if (this.#thumbnailCache.has(currentPageNumber)) {
            return false; // 如果已经缓存了缩略图，则直接返回
        }
        const page = await that.pdfDocument.getPage(currentPageNumber);
        const viewport = page.getViewport({scale});

        // 重用 Canvas，减少 DOM 操作
        const canvas = that.thumbnailCanvas || document.createElement('canvas');
        that.thumbnailCanvas = canvas; // 保存用于重用
        canvas.width = viewport.width;
        canvas.height = viewport.height;

        const context = canvas.getContext('2d');
        const renderContext = {canvasContext: context, viewport};

        // 渲染页面
        await page.render(renderContext).promise;

        // 转换 Canvas 为 Blob
        const thumbnailBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg'));
        const thumbnailUrl = URL.createObjectURL(thumbnailBlob);

        // 缓存缩略图 URL
        that.#thumbnailCache.set(currentPageNumber, thumbnailUrl);

        // 创建缩略图并插入 DOM
        const thumbnailDiv = that.getThumbnailDiv(currentPageNumber);
        const imgElement = that.createThumbnailImage(thumbnailUrl, currentPageNumber);

        thumbnailDiv.appendChild(imgElement);

        // 设置缩略图的点击事件
        that.addClickEventListener(thumbnailDiv, currentPageNumber);

        // 释放 Blob URL 以避免内存泄漏
        imgElement.onload = () => {
            URL.revokeObjectURL(thumbnailUrl); // 当图片加载完毕后释放 URL
        };

    }

    /**
     * 获取缩略图容器
     * @param pageNumber
     * @returns {Element}
     */
    getThumbnailDiv(pageNumber) {
        let thumbnailDiv = document.querySelector(`.drawer-item[data-page="${pageNumber}"]`);
        if (!thumbnailDiv) {
            thumbnailDiv = document.createElement('div');
            thumbnailDiv.classList.add('drawer-item');
            thumbnailDiv.dataset.page = pageNumber;
            document.querySelector('.drawer').appendChild(thumbnailDiv); // 假设有一个父容器是 .drawer
        }
        return thumbnailDiv;
    }

    /**
     * 创建缩略图
     * @param thumbnailUrl
     * @param pageNumber
     * @returns {HTMLImageElement}
     */
    createThumbnailImage(thumbnailUrl, pageNumber) {
        const imgElement = document.createElement('img');
        imgElement.classList.add('thumbnail');
        imgElement.src = thumbnailUrl;
        imgElement.alt = `Page ${pageNumber} thumbnail`; // 增加 alt 提高可访问性
        imgElement.dataset.page = pageNumber;
        imgElement.style.width = '80%';
        imgElement.style.height = 'auto';
        return imgElement;
    }

    /**
     * 添加点击事件
     * @param thumbnailDiv
     * @param pageNumber
     */
    addClickEventListener(thumbnailDiv, pageNumber) {
        // 设置是否为当前活动页
        thumbnailDiv.classList.toggle('active', parseInt(this.pageNumber) === parseInt(pageNumber));

        thumbnailDiv.addEventListener('click', async (event) => {
            const target = event.target.closest('.drawer-item');
            if (target) {
                document.querySelectorAll('.drawer-item').forEach(item => {
                    item.classList.remove('active');
                });
                target.classList.add('active');
                this.pageNumber = parseInt(target.dataset.page);
                await this.switchPage(this.pageNumber);
            }
        }, {passive: true});
    }

    /**
     * 显示 loading 状态
     * @param pageNumber
     */
    showLoading(pageNumber) {
        let pageContainer = document.querySelector(`.page-container[data-page="${pageNumber}"]`);
        pageContainer?.classList.add('loading');

    }

    /**
     * 隐藏 loading 状态
     * @param pageNumber
     */
    hideLoading(pageNumber) {
        let pageContainer = document.querySelector(`.page-container[data-page="${pageNumber}"]`);
        pageContainer?.classList.remove('loading');
    }

    /**
     * 绘制当前渲染已经渲染pdf签批层
     * @returns {Promise<void>}
     */
    async drawSignLayer() {
        let pageArray = Array.from(this.#renderPagesCache);
        if (pageArray.length <= 0) {
            return;
        }
        if (this.isAnnotationLayer === false) {
            return;
        }
        for (let page of pageArray) {
            let pageCanvas = document.querySelector(`.page-canvas[data-page="${page}"]`);
            if (pageCanvas && !this.signatureList.has(page)) {
                await this.createLayerDom(page);
            } else {
                document.querySelector(`.draw-canvas[data-page="${page}"]`).style.pointerEvents = 'auto';
            }
        }
    }

    /**
     * 创建签批层
     * @param pageNumber
     * @returns {Promise<void>}
     */
    async createLayerDom(pageNumber) {
        let pageCanvas = document.querySelector(`.page-canvas[data-page="${pageNumber}"]`);
        if (pageCanvas) {
            let drawCanvas = document.createElement('canvas');
            drawCanvas.classList.add('draw-canvas');
            drawCanvas.dataset.page = pageNumber.toString();
            drawCanvas.style.pointerEvents = 'none';
            drawCanvas.style.transform = 'rotate(0deg) scale(1, 1)';
            drawCanvas.style.touchAction = 'none';
            drawCanvas.style.userSelect = 'none';
            drawCanvas.style.width = '100%';
            drawCanvas.style.height = '100%';

            const ratio = Math.max(window.devicePixelRatio || 1, 1);
            drawCanvas.width = pageCanvas.offsetWidth * ratio;
            drawCanvas.height = pageCanvas.offsetHeight * ratio;
            drawCanvas.getContext('2d', {willReadFrequently: true}).scale(ratio, ratio);
            pageCanvas.insertAdjacentElement('afterend', drawCanvas);
            await this.drawAnnotationLayer(pageNumber, pageCanvas, drawCanvas);

        }
    }

    /**
     * 签批层绘制
     * @returns {Promise<void>}
     */
    drawAnnotationLayer(pageNumber, pageCanvas, drawCanvas) {
        let that = this;
        let singature = new SignaturePad(drawCanvas, {
            minWidth: that.minLineWidth,
            maxWidth: that.maxLineWidth,
            backgroundColor: 'rgba(255, 255, 255, 0)',
            penColor: 'red',
            pointerType: that.penType,
        });

        if (this.#annotateDataCache.has(pageNumber)) {
            singature.fromDataURL(this.#annotateDataCache.get(pageNumber));
        }

        that.signatureList.set(pageNumber, singature);

        if (that.isAnnotationLayer) {
            drawCanvas.style.pointerEvents = 'auto';
            if (that.isEraser) {
                that.signatureList.get(pageNumber).compositeOperation = 'destination-out';
            } else {
                that.signatureList.get(pageNumber).compositeOperation = 'source-over';
            }
        } else {
            drawCanvas.style.pointerEvents = 'none';
        }
        drawCanvas.addEventListener('pointerup', this.pointerUpHandler.bind(that, pageNumber, drawCanvas, singature), {passive: true});
        drawCanvas.addEventListener('pointerdown', this.pointerDownHandler.bind(that, pageNumber, drawCanvas), {passive: true});
        drawCanvas.addEventListener('pointermove', this.pointerMoveHandler.bind(that, pageNumber, drawCanvas), {passive: true});
    }

    /**
     * 卸载签批层
     * @param pageNumber
     */
    removeLayerDom(pageNumber) {
        let drawCanvas = document.querySelector(`.draw-canvas[data-page="${pageNumber}"]`);
        if (drawCanvas) {
            let drawContext = drawCanvas.getContext('2d');
            drawContext.clearRect(0, 0, drawCanvas.width, drawCanvas.height);
            // 移除事件监听器
            drawCanvas.removeEventListener('pointerup', this.pointerUpHandler);
            drawCanvas.removeEventListener('pointerdown', this.pointerDownHandler);
            drawCanvas.removeEventListener('pointermove', this.pointerMoveHandler);

            // 从DOM中移除drawCanvas元素
            drawCanvas.remove();

            // 从signatureList中移除该页的Signature对象
            this.signatureList.delete(parseInt(pageNumber));
        }
    }

    /**
     * 关闭签批 删除未绘制签批层
     */
    closeSignLayer() {
        let that = this;
        let drawCanvasArray = document.querySelectorAll('.draw-canvas');
        drawCanvasArray.forEach(function (drawCanvas) {
            if (that.#annotateDataCache.has(parseInt(drawCanvas.dataset.page))) {
                drawCanvas.style.pointerEvents = 'none';
            } else {
                that.removeLayerDom(drawCanvas.dataset.page);
            }
        });
    }

    /**
     * pointerMove事件处理程序
     * @param pageNumber
     * @param drawCanvas
     * @param singature
     * @param event
     */
    pointerUpHandler(pageNumber, drawCanvas, singature, event) {
        let that = this;
        if (that.penType !== event.pointerType) {
            return;
        }
        if (that.signatureList.size > 0 && that.signatureList.get(pageNumber).compositeOperation === 'destination-out') {
            that.touchCircle.style.display = 'none';
        }
        // 计算画布是否为空
        let imageData = drawCanvas.getContext('2d').getImageData(0, 0, drawCanvas.width, drawCanvas.height).data;
        let transparentPixelFound = false;
        let pixelCount = imageData.length;
        for (let j = 0; j < pixelCount; j += 4) {
            if (imageData[j + 3] !== 0) {
                transparentPixelFound = true;
                break;
            }
        }
        if (transparentPixelFound) {
            that.#annotateDataCache.set(pageNumber, singature.toDataURL('image/png'));
        } else {
            if (that.#annotateDataCache.has(pageNumber)) {
                that.#annotateDataCache.delete(pageNumber);
            }
        }
        // 更新保存按钮状态
        if (!that.#annotateDataCache.size) {
            that.saveButton.classList.add('disabled');
        } else {
            that.saveButton.classList.remove('disabled');
        }
    }

    /**
     * pointerDown事件处理程序
     * @param pageNumber
     * @param drawCanvas
     * @param event
     */
    pointerDownHandler(pageNumber, drawCanvas, event) {
        let that = this;
        if (that.penType !== event.pointerType) {
            return;
        }

        if (that.signatureList.size > 0 && that.signatureList.get(pageNumber).compositeOperation === 'destination-out' && that.isEraser) {
            const x = event.clientX - that.touchCircle.offsetWidth / 2;
            const y = event.clientY - that.touchCircle.offsetHeight / 2;
            that.touchCircle.style.left = x + 'px';
            that.touchCircle.style.top = y + 'px';
            that.touchCircle.style.display = 'block';
        }
    }

    /**
     * pointerMove事件处理程序
     * @param pageNumber
     * @param drawCanvas
     * @param event
     */
    pointerMoveHandler(pageNumber, drawCanvas, event) {
        let that = this;
        if (that.signatureList.size > 0 && that.signatureList.get(pageNumber).compositeOperation === 'destination-out' && that.isEraser) {
            let circleWidth = that.touchCircle.offsetWidth;
            let circleHeight = that.touchCircle.offsetHeight;
            let x = event.clientX - circleWidth / 2;
            let y = event.clientY - circleHeight / 2;
            that.touchCircle.style.left = x + 'px';
            that.touchCircle.style.top = y + 'px';
        }
    }


    /**
     * 切换页面
     * @param status
     * @returns {Promise<void>}
     */
    async switchPage(status = true) {
        if (this.pageNumber === 1 && this.preNumber === 0) {
            return;
        }
        // 确保当前页码有效
        if (this.pageNumber < 1 || this.pageNumber > this.totalPages) {
            return;
        }
        document.getElementById('current-page').textContent = this.pageNumber;
        if (status) {
            let thumbnail = document.querySelector(`.page-container[data-page="${this.pageNumber}"]`);
            if (thumbnail) {
                thumbnail.scrollIntoView({
                    behavior: 'instant', block: 'start',
                })
            }
        }
        this.changePageButton();
        let upOrDown = this.pdfPageUpOrDown();
        if (upOrDown !== undefined) {
            // 首页
            let firstPage = parseInt(this.#renderPagesCache.values().next().value);
            // 尾页
            let finallyPage = parseInt(Array.from(this.#renderPagesCache).pop());
            // 下滑卸载 DOM
            if (upOrDown === 'down' && this.pageNumber > this.preloadPages) {
                let canvas = document.querySelector(`.page-canvas[data-page="${firstPage}"]`);
                if (canvas) {
                    canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
                    canvas.remove();
                    canvas = null;
                }
                this.#renderPagesCache.delete(firstPage);

            }
            // 上滑卸载 DOM
            if (upOrDown === 'up' && this.pageNumber < (this.totalPages - this.preloadPages)) {
                let canvas = document.querySelector(`.page-canvas[data-page="${finallyPage}"]`);
                if (canvas) {
                    canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
                    canvas.remove();
                    canvas = null;
                }
                this.#renderPagesCache.delete(finallyPage);
            }
            await this.preloadPagesAround(this.pageNumber);
        }
        if (this.drawerOpen) {
            this.changeThumbnailActive(this.pageNumber);
        }
    }

    /**
     * 判断页面上下滑
     * @returns {string}
     */
    pdfPageUpOrDown() {
        if (parseInt(this.preNumber) !== 0 && parseInt(this.pageNumber) > parseInt(this.preNumber)) {
            return 'down';
        } else if (parseInt(this.preNumber) !== 0 && parseInt(this.pageNumber) < parseInt(this.preNumber)) {
            return 'up';
        }
    }

    /**
     * 创建页面dom
     * @param pageNumber
     * @returns {Promise<{pageCanvas: HTMLCanvasElement, drawCanvas: HTMLCanvasElement}>}
     */
    async createPageDom(pageNumber) {
        let pageContainer = document.querySelector('.page-container[data-page="' + pageNumber + '"]');
        if (pageContainer) {
            // 创建页面画布
            const pageCanvas = document.createElement('canvas');
            pageCanvas.style.display = 'none';
            pageCanvas.classList.add('page-canvas');
            pageCanvas.dataset.page = pageNumber;
            pageCanvas.style.transform = 'rotate(0deg) scale(1, 1)';
            if (this.pageZoom === 'page-width') {
                pageCanvas.style.width = '100%';
                pageCanvas.style.height = 'auto';
            } else {
                if (this.pdfWidth / this.pdfHeight < this.window.width / this.window.height) {
                    pageCanvas.style.width = 'auto';
                    pageCanvas.style.height = '100%';
                } else {
                    pageCanvas.style.width = '100%';
                    pageCanvas.style.height = 'auto';
                }
            }
            pageContainer.appendChild(pageCanvas);

            return {
                pageCanvas
            };
        } else {
            return false;
        }

    }

    /**
     * 页面切换出发缩略图激活
     * @param currentPageNumber
     */
    changeThumbnailActive(currentPageNumber) {
        document.querySelectorAll('.drawer-item').forEach(item => {
            let page = parseInt(item.getAttribute('data-page'));
            item.classList.toggle('active', parseInt(page) === parseInt(currentPageNumber));
        });
        setTimeout(() => {
            Viewer.scrollToCurrentPage(currentPageNumber, 'smooth');
        }, 100)

    }

    /**
     * 页面滚动
     * @param currentPageNumber
     * @param behavior
     */
    static scrollToCurrentPage(currentPageNumber, behavior) {
        let thumbnail = document.querySelector(`.drawer-item[data-page="${currentPageNumber}"]`);
        if (thumbnail) {
            requestAnimationFrame(() => {
                thumbnail.scrollIntoView({
                    behavior: behavior, block: 'center',
                })
            })
        }
    }

    /**
     * 设置pdf背景色
     * @param color
     */
    setPdfBackgroundColor(color) {
        this.pdfBackgroundColor = color;
        let cachePage = Array.from(this.#renderPagesCache);
        if (cachePage.length > 0) {
            for (let currentPageNumber of cachePage) {
                this.pdfDocument.getPage(currentPageNumber).then(async page => {
                    let viewport = page.getViewport({scale: this.scale});
                    let thisPageCanvas = document.querySelector(`.page-canvas[data-page="${currentPageNumber}"]`);
                    let thisCtx = thisPageCanvas.getContext('2d', {alpha: false});
                    let renderContext = {
                        canvasContext: thisCtx,
                        transform: this.transform,
                        viewport: viewport,
                        background: this.pdfBackgroundColor
                    }
                    await page.render(renderContext).promise;
                })
            }
        }
    }

    /**
     * 页面缩放
     * @returns {Promise<void>}
     */
    async zoomPage() {

        const that = this;
        let pageNumber = this.pageNumber;
        const windowWidth = $(window).width();
        const windowHeight = $(window).height();
        const adjustedWindowWidth = this.drawerOpen ? windowWidth - (windowWidth < 767 ? 75 : 150) : windowWidth;

        // 提取 DOM 查询，避免重复查询
        const pageContainers = document.querySelectorAll('.page-container');
        const btnZoomText = $('#btn-zoom').find('.btn-text');
        const pageNum = document.querySelector('.page-num');

        // 计算页面大小
        const calculateHeightByWidth = (width) => {
            return Viewer.defaultPage.height * (width / Viewer.defaultPage.width);
        };

        const calculateWidthByHeight = (height) => {
            return Viewer.defaultPage.width * (height / Viewer.defaultPage.height);
        };

        const setPageContainerSize = (width, height) => {
            // 只需要一次性更新所有页面容器的大小
            pageContainers.forEach((item) => {
                const currentPageNumber = item.getAttribute('data-page');
                const currentViewport = that.#pageViewportCache.get(parseInt(currentPageNumber));

                let finalHeight = height;

                if (that.pageZoom === "page-width" && currentViewport.width > currentViewport.height) {
                    finalHeight = (adjustedWindowWidth / currentViewport.width) * currentViewport.height;
                } else if (that.pageZoom === "page-fill" && currentViewport.width > currentViewport.height && adjustedWindowWidth > windowHeight) {
                    finalHeight = (width / currentViewport.width) * currentViewport.height;
                } else if (that.pageZoom === "page-fill" && currentViewport.width > currentViewport.height && adjustedWindowWidth < windowHeight) {
                    finalHeight = (adjustedWindowWidth / currentViewport.width) * currentViewport.height;
                }
                // 批量设置页面容器的宽高
                item.style.width = `${width}px`;
                item.style.height = `${finalHeight}px`;

                // 更新 Canvas 的宽高
                const pageCanvas = item.querySelector('.page-canvas');
                if (pageCanvas) {
                    pageCanvas.style.width = `${width}px`;
                    pageCanvas.style.height = `${finalHeight}px`;
                }

                const drawCanvas = item.querySelector('.draw-canvas');
                const ratio = Math.max(window.devicePixelRatio || 1, 1);
                if (drawCanvas) {
                    let currentCanvasWidth = drawCanvas.offsetWidth * ratio;
                    let currentCanvasHeight = drawCanvas.offsetHeight * ratio;
                    drawCanvas.width = currentCanvasWidth;
                    drawCanvas.height = currentCanvasHeight;
                    drawCanvas.getContext("2d", {willReadFrequently: true}).scale(ratio, ratio);
                    if (that.#annotateDataCache.has(parseInt(currentPageNumber))) {
                        that.signatureList.get(parseInt(currentPageNumber)).fromDataURL(that.#annotateDataCache.get(parseInt(currentPageNumber)));
                    }
                }
            });
            that.skipPage(pageNumber);
        };

        // 根据不同的缩放模式调整页面大小
        if (this.pageZoom === 'page-width') {
            this.viewerContainer.style.width = `${adjustedWindowWidth}px`;
            const pageHeight = calculateHeightByWidth(adjustedWindowWidth);
            setPageContainerSize(adjustedWindowWidth, pageHeight);

            pageNum.style.color = '#00000080';
            btnZoomText.find('.btn-text').text('适合页宽');
        } else if (this.pageZoom === 'page-fill') {
            if (adjustedWindowWidth > windowHeight) {
                requestAnimationFrame(() => {
                    const defaultHeight = windowHeight - 92;
                    const pageWidth = calculateWidthByHeight(defaultHeight);
                    setPageContainerSize(pageWidth, defaultHeight);
                });
                pageNum.style.color = '#fff';
            } else {
                requestAnimationFrame(() => {
                    const pageHeight = calculateHeightByWidth(adjustedWindowWidth);
                    setPageContainerSize(adjustedWindowWidth, pageHeight);
                    pageNum.style.color = '#00000080';
                });
            }
            btnZoomText.find('.btn-text').text('适合页面');
        }

    }

    /**
     * 更新画笔大小
     */
    updateBrushSize() {
        this.signatureList.forEach((signature, k) => {
            // 获取当前页面的 canvas 元素
            let customCanvas = document.querySelector(`.draw-canvas[data-page="${k}"]`);
            if (customCanvas == null) {
                return; // 如果没有找到对应的 canvas，跳过
            }

            // 获取 canvas 的宽度和高度
            let currentCustomCanvasWidth = customCanvas.offsetWidth;
            let currentCustomCanvasHeight = customCanvas.offsetHeight;

            // 计算宽高比例
            let widthRatio = currentCustomCanvasWidth / this.pdfWidth;
            let heightRatio = currentCustomCanvasHeight / this.pdfWidth;

            // 根据是否是橡皮擦，设置 minWidth 和 maxWidth
            if (this.isEraser) {
                signature.minWidth = 20;
                signature.maxWidth = 20;
            } else {
                signature.minWidth = this.minLineWidth * Math.min(widthRatio, heightRatio);
                signature.maxWidth = this.maxLineWidth * Math.min(widthRatio, heightRatio);
            }

            // 更新 signatureList 中对应项
            this.signatureList.set(k, signature);
        });

    }

    /**
     * 修改翻页按钮状态
     */
    changePageButton() {
        let btnPrevPage = document.getElementById('btn-prev-page');
        let btnNextPage = document.getElementById('btn-next-page');
        let btnGotoPage = document.getElementById('btn-goto-page');

        // 判断上一页状态
        if (this.pageNumber <= 1) {
            btnPrevPage.classList.add('disabled');
        } else {
            btnPrevPage.classList.remove('disabled');
        }

        // 判断下一页状态
        if (this.pageNumber < this.totalPages) {
            btnNextPage.classList.remove('disabled');
        } else {
            btnNextPage.classList.add('disabled');
        }

        // 判断跳转到状态
        if (this.totalPages <= 1) {
            btnGotoPage.classList.add('disabled');
        } else {
            btnGotoPage.classList.remove('disabled');
        }
    }


    /**
     * 四舍五入
     * @param x
     * @param div
     * @returns {*|number}
     */
    roundToDivide(x, div) {
        const r = x % div;
        return r === 0 ? x : Math.round(x - r + div);
    }

    /**
     * 获取输出比例
     * @returns {{scaled: boolean, sx: (number|number), sy: (number|number)}}
     */
    getOutputScale() {
        let pixelRatio = 'devicePixelRatio' in window ? window.devicePixelRatio : 1;
        return {
            sx: pixelRatio, sy: pixelRatio, scaled: pixelRatio !== 1
        };
    }

    /**
     * 通过法里序列算法（最大阶数为8）将浮点数近似为分数。
     */
    approximateFraction(x) {
        if (Math.floor(x) === x) {
            return [x, 1];
        }
        const xinv = 1 / x;
        const limit = 8;
        if (xinv > limit) {
            return [1, limit];
        } else if (Math.floor(xinv) === xinv) {
            return [1, xinv];
        }
        const x_ = x > 1 ? xinv : x;
        let a = 0, b = 1, c = 1, d = 1;
        while (true) {
            const p = a + c, q = b + d;
            if (q > limit) {
                break;
            }
            if (x_ <= p / q) {
                c = p;
                d = q;
            } else {
                a = p;
                b = q;
            }
        }
        let result;
        if (x_ - a / b < c / d - x_) {
            result = x_ === x ? [a, b] : [b, a];
        } else {
            result = x_ === x ? [c, d] : [d, c];
        }
        return result;
    }

    /**
     * 获取当前页面附近页面
     * @param input
     * @param length
     * @returns {unknown[]|*[]}
     */
    generateNumberRange(input, length = 9) {
        // 中位数
        let mid = Math.floor((length) / 2);
        if (input <= mid) {
            return Array.from({length: length}, (_, i) => i + 1);
        }
        if (input >= (this.totalPages - mid)) {
            return Array.from({length: length}, (_, i) => this.totalPages - length + 1 + i);
        }
        let result = [];
        for (let i = input - mid; i <= input + mid; i++) {
            result.push(i);
            if (result.length === length) {
                break;
            }
        }
        return result;
    }

    /**
     * 对话框工具
     * @param dialogId
     * @param show
     */
    dialogTools(dialogId, show) {
        let that = this;
        // 对话框元素
        let dialog = document.getElementById(dialogId);
        if (dialog) {
            if (show) {
                // 添加遮罩层
                let mask = document.createElement('div');
                mask.id = 'mask';
                mask.classList.add('mask');
                mask.classList.add('dialog-fadeIn');
                document.body.append(mask);
                mask.addEventListener('click', function () {
                    that.dialogTools(dialogId, false);
                });
                dialog.classList.remove('dialog-fadeOut');
                dialog.classList.add('dialog-fadeIn');
                dialog.style.display = 'block';
                // 关闭按钮
                let btnClose = dialog.getElementsByClassName('btn-close');
                if (btnClose) {
                    btnClose[0].addEventListener('click', function () {
                        that.dialogTools(dialogId, false);
                    }, {once: true});
                }
            } else {
                dialog.classList.remove('dialog-fadeIn');
                dialog.classList.add('dialog-fadeOut');
                // 遮罩层
                let mask = document.getElementById('mask');
                mask.classList.remove('dialog-fadeIn');
                mask.classList.add('dialog-fadeOut');
                setTimeout(function () {
                    dialog.style.display = 'none';
                    mask.remove();
                }, 280);
            }
        }
    }

    /**
     * 跳转到指定页
     * @param pageNumber
     */
    skipPage(pageNumber) {
        if (pageNumber < 1 || pageNumber > this.totalPages) {
            return;
        }
        this.pageNumber = pageNumber;
        const goPageElement = document.querySelector('.page-container[data-page="' + pageNumber + '"]');
        if (goPageElement) {
            this.viewerContainer.scrollTo({
                top: goPageElement.offsetTop, behavior: 'auto' // 使用平滑滚动
            });
        }
    }


    /**
     * 渲染缩略图
     */
    async renderThumbnails() {
        for (let i = 1; i <= this.totalPages; i++) {
            await this.generateThumbnail(i, 0.2)
        }
    }

    /**
     * 保存pdf文件到本地
     * @returns {Promise<void>}
     */
    async savePdfFile() {
        let blob = await this.savePdf();
        this.downloadBlob(blob, "pdfjs_internal_editor.pdf");
    }


    /**
     * 将 Blob 对象保存到本地(测试使用)
     * @param blob
     * @param fileName
     */
    downloadBlob(blob, fileName) {
        const downloadUrl = URL.createObjectURL(blob);
        const downloadLink = document.createElement('a');
        downloadLink.href = downloadUrl;
        downloadLink.download = fileName;
        document.body.appendChild(downloadLink);
        downloadLink.click();
        document.body.removeChild(downloadLink);
        // 清理临时 URL
        URL.revokeObjectURL(downloadUrl);
    }

    /**
     * 构建注解存储参数
     * @param pageNumber
     * @returns {Promise<void>}
     */
    async buildAnnotationStorageParams(pageNumber) {
        let page = await this.pdfDocument.getPage(parseInt(pageNumber));
        let viewport = page.getViewport({scale: this.scale});
        let signature = this.signatureList.get(pageNumber);
        if (!signature.isEmpty()) {
            let base64Data = signature.toDataURL();
            if (base64Data) {
                let blob = this.base64ToBlob(base64Data, 'image/png');
                let imageBitmap = await createImageBitmap(blob);
                let url = new URL(URL.createObjectURL(blob));
                let param = url.pathname.split('/').pop();
                this.writeAnnotationStorage(`pdfjs_internal_editor_${pageNumber - 1}`, {
                    "annotationType": 13,
                    "bitmap": imageBitmap,
                    "bitmapId": `image_${param}_0`,
                    "pageIndex": pageNumber - 1,
                    "rotation": viewport.rotation,
                    "rect": [0, 0, viewport.rawDims.pageWidth, viewport.rawDims.pageHeight],
                    "bitmapFile": signature.toDataURL(),
                    "isSvg": false,
                    "structTreeParentId": undefined
                });
            }
        }
    }

    /**
     * 写入注解
     * @param key
     * @param value
     */
    writeAnnotationStorage(key, value) {
        this.pdfDocument.annotationStorage.setValue(key, value);
    }

    /**
     * base64转blob
     * @param base64Data
     * @param contentType
     * @returns {Blob}
     */
    base64ToBlob(base64Data, contentType = 'application/octet-stream') {
        let byteString;
        if (base64Data.split(',')[0].indexOf('base64') >= 0) {
            byteString = atob(base64Data.split(',')[1]);
        } else {
            byteString = unescape(encodeURIComponent(base64Data.split(',')[1]));
        }

        let mimeString = contentType;
        let ia = new Uint8Array(byteString.length);
        for (let i = 0; i < byteString.length; i++) {
            ia[i] = byteString.charCodeAt(i);
        }
        return new Blob([ia], {type: mimeString});
    }

    /**
     * 保存pdf文件
     * @returns {Promise<unknown>}
     */
    async savePdf() {
        let that = this;
        return await new Promise((resolve, reject) => {
            let promises = [];
            Array.from(this.#annotateDataCache.keys()).forEach((dataPageNUmber) => {
                const annotationPromise = that.buildAnnotationStorageParams(dataPageNUmber);
                promises.push(annotationPromise);  // 推送异步任务的 Promise
            });
            Promise.all(promises).then(() => {
                that.pdfDocument.saveDocument().then(function (data) {
                    resolve(new Blob([data], {type: 'application/pdf'}));
                });
            }).catch(reject);
        });
    }
}


export default Viewer;